package model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;

public class SampleGenerator {
	
	public static ArrayList<FrequencyPair> generateSampleMeanDistribution(ArrayList<FrequencyPair> distribution, int n){
		
		// 1.) Filter the list to remove the one's with 0 frequency.
		ArrayList<FrequencyPair> filteredDist = createCopyWithFreqGreaterThanZero(distribution, n);
		
		if(filteredDist.size()  == 0)
			return new ArrayList<FrequencyPair>();
		
		// 2.) Generate all sample means
		LinkedHashMap<Double, Long> meanDistributionMap = new LinkedHashMap<Double, Long>();
		ArrayList<Node> fringe = new ArrayList<Node>();
		fringe.add(new Node(filteredDist.get(0).getNum(), 0));
	
		while(!fringe.isEmpty()){
			Node curr = fringe.remove(0);
			
			if(curr.getDepth() == n){ 
				//stop expanding and add mean to the distribution
				Double avg = curr.getCurrAverage();
				Long freq = meanDistributionMap.get(avg);
				
				if(freq == null)
					meanDistributionMap.put(avg, new Long(1));
				else
					meanDistributionMap.put(avg,  freq+1);
			}
			else if(curr.getDepth() < n){
				for(FrequencyPair fp: filteredDist)
					fringe.add(0, new Node(fp.getNum() + curr.getRunningTotal(), curr.getDepth() + 1));
			}
		}
		
		//3.) Convert from linked hashmap to arraylist and sort
		ArrayList<FrequencyPair> meanDistribution = new ArrayList<FrequencyPair>();
		Iterator<Double> iterator = meanDistributionMap.keySet().iterator();
		
		while(iterator.hasNext()){
			Double currKey = iterator.next();
			meanDistribution.add(new FrequencyPair(currKey, new BigDecimal(meanDistributionMap.get(currKey))));
		}
		Collections.sort(meanDistribution);
		
		return meanDistribution;
	}
	
	private static ArrayList<FrequencyPair> createCopyWithFreqGreaterThanZero(ArrayList<FrequencyPair> distribution, int n){
		
		ArrayList<FrequencyPair> copy = new ArrayList<FrequencyPair>();
		
		for(FrequencyPair fp: distribution)
			if(fp.getFrequency().compareTo(BigDecimal.ZERO) > 0)
				copy.add(fp.getCopy());
		
		return copy;
	}
}

class Node{
	
	private int runningTotal;
	private int depth;
	
	public Node(int runningTotal, int depth){
		this.runningTotal = runningTotal;
		this.depth = depth;
	}
	
	public int getRunningTotal(){
		return runningTotal;
	}
	
	public double getCurrAverage(){
		return 1.0*runningTotal/depth;
	}
	
	public int getDepth(){
		return depth;
	}
}
